package com.kdg.javaEight;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * Created by kong on 2017/8/31.
 * 数据流的不同类型
 * 数据流的处理顺序
 * 数据流的复杂操作：collect flatMap reduce
 */
public class TestDataStream {
    public static void main(String[] args) {
        //数据流表示元素的序列，并支持不同种类的操作来执行元素上的计算
        List<String> list = Arrays.asList("a1", "a2", "b1", "c2", "c1");

//        数据流操作要么是衔接操作，要么是终止操作。衔接操作返回数据流，终止操作无返回值，或者返回一个不是流的结果。

        list.stream()
                .filter(s -> s.startsWith("a") || s.startsWith("c"))
                .map(String :: toUpperCase)
                .sorted()
                .forEach(System.out::println);
        //基本数据流
        IntStream.range(2,8)
                .sorted().forEach(System.out::println);
        //基本数据流可以进行一些聚合操作   sum   average
        IntStream.range(2,10)
                .average().ifPresent(System.out::println);
        //对象数据流
        IntStream.range(3,5)
                .asDoubleStream().forEach(System.out::println);
        //基本数据流可以转换为对象数据流
        Stream.of(1.0, 2.0, 3.0)
                .mapToInt(Double::intValue)
                .mapToObj(i -> "a" + i)
                .forEach(System.out::println);
        //衔接操作重要特性是延迟性。衔接操作只会在终止操作执行后才会被调用
        /*Stream.of("d2", "a2", "b1", "b3", "c")
                .filter(s -> {
                    System.out.println("filter: " + s);
                    return true;
                });
        Stream.of("d2", "a2", "b1", "b3", "c")
                .filter(s -> {
                    System.out.println("filter: " + s);
                    return true;
                }).forEach(System.out::println);*/
        /*Stream.of("d2", "a2", "b1", "b3", "c")
                .filter(s -> {
                    System.out.println("filter: " + s);
                    return true;
                })
                .anyMatch(s -> s.startsWith("a"));*/

        //排序是一类特殊的衔接操作。它是有状态的操作，因为你需要在处理中保存状态来对集合中的元素排序。
        /*Stream.of("d2", "a2", "b1", "b3", "c")
                .sorted((s1, s2) -> {
                    System.out.printf("sort: %s; %s\n", s1, s2);
                    return s1.compareTo(s2);
                })
                .filter(s -> {
                    System.out.println("filter: " + s);
                    return s.startsWith("a");
                })
                .map(s -> {
                    System.out.println("map: " + s);
                    return s.toUpperCase();
                })
                .forEach(s -> System.out.println("forEach: " + s));*/
        /*Stream.of("d2", "d2", "b1", "d2", "d")
                .distinct()
                    .forEach(System.out::print);*/

        ArrayList<Man> men = new ArrayList<>();
        men.add(new Man("Max", 18));
        men.add(new Man("Peter", 23));
        men.add(new Man("Pamela", 23));
        men.add(new Man("David", 12));

        //collectcollect是非常有用的终止操作，将流中的元素存放在不同类型的结果中，例如List、Set或者Map

        List<Man> p = men.stream()
                .filter(s -> s.getName().startsWith("P"))
                .collect(Collectors.toList());
//        p.stream().forEach(a -> System.out.print(a.getName()));
        //collect分组
        /*Map<Integer, List<Man>> collect = men.stream()
                .collect(Collectors.groupingBy(e -> e.getAge()));
        collect.forEach((k,v) -> {
            System.out.println(k);
            v.stream().forEach(m -> System.out.println(m.getName()));
        });*/
        //collect进行聚合操作
        /*Double collect = men.stream()
                .collect(Collectors.averagingInt(e -> e.getAge()));
        System.out.println(collect);*/

        List<Foo> foos = new ArrayList<>();
        IntStream.range(1,4)
                .forEach(i -> foos.add(new Foo("Fool"+i)));

        foos.stream()
                .forEach(f -> {
                    IntStream.range(1,4).forEach(
                        i -> f.bars.add(new Bar("Bar"+i))
                     );
//                    System.out.println(f);
                }
                    );
        //flatMap将流中的每个元素，转换为其它对象的流。
        foos.stream()
                .flatMap(f -> f.bars.stream())
                .forEach(b -> System.out.println(b));
        //归约操作将所有流中的元素组合为单一结果。
        men.stream()
                .reduce((p1, p2) -> p1.age > p2.age ? p1 : p2)
                .ifPresent(System.out::println);

        Man reduceMan = men.stream()
                .reduce(new Man("", 5), (p1, p2) -> {
                    p1.age += p2.age;
                    p1.name += p2.name;
                    return p1;
                });
        ForkJoinPool forkJoinPool = ForkJoinPool.commonPool();
//        System.out.println(forkJoinPool.getParallelism());  //公共池的默认初始值
        men
            .parallelStream()
            .reduce(0,
                    (sum, f) -> {
                        System.out.format("accumulator: sum=%s; person=%s [%s]\n",
                                sum, f, Thread.currentThread().getName());
                        return sum += f.age;
                    },
                    (sum1, sum2) -> {
                        System.out.format("combiner: sum1=%s; sum2=%s [%s]\n",
                                sum1, sum2, Thread.currentThread().getName());
                        return sum1 + sum2;
                    });

    }
}

class Man{
    String name;
    int age;

    public Man(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Man{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

class Foo{
    String name;
    List<Bar> bars = new ArrayList<Bar>();

    public Foo(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Foo{" +
                "name='" + name + '\'' +
                ", bars=" + bars +
                '}';
    }
}
class Bar{
    String name;

    public Bar(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Bar{" +
                "name='" + name + '\'' +
                '}';
    }
}